package com.alipay.sdk.util;

import android.app.Application;
import android.os.Environment;
import android.text.TextUtils;
import android.util.Log;

import com.elvishew.xlog.LogConfiguration;
import com.elvishew.xlog.LogLevel;
import com.elvishew.xlog.XLog;
import com.elvishew.xlog.flattener.DefaultFlattener;
import com.elvishew.xlog.printer.file.FilePrinter;
import com.elvishew.xlog.printer.file.backup.NeverBackupStrategy;
import com.elvishew.xlog.printer.file.clean.FileLastModifiedCleanStrategy;
import com.elvishew.xlog.printer.file.naming.DateFileNameGenerator;

import java.io.File;
import java.text.SimpleDateFormat;

/**
 * author: bd-001935
 * date: 2/21/22
 * 日志工具类
 */
public class LogUtil {

    private static boolean enableLog = false;
    private static final int CHUNK_SIZE = 4000;
    private static final String TAG = "BDAppBaseLogging";
    private static boolean useXlog = false;

    private enum TYPE {
        debug,
        info,
        error
    }

    public static void init(boolean enableLog) {
        setEnableLog(enableLog);
        useXlog = false;
    }

    /**
     * 初始化xlog
     *
     * @param application
     * @param logTag
     * @param logFileName
     */
    public static void init(Application application, boolean enableLog, String logTag, String logFileName) {

        setEnableLog(enableLog);
        useXlog = true;
        LogConfiguration config = new LogConfiguration.Builder()
                .logLevel(LogLevel.ALL)
                .tag(logTag)
                .build();
        String defaultDir;
        String FILE_SEP = System.getProperty("file.separator");
        if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())
                && application.getExternalCacheDir() != null)
            defaultDir = application.getExternalCacheDir() + FILE_SEP + logFileName + FILE_SEP;
        else {
            defaultDir = application.getCacheDir() + FILE_SEP + logFileName + FILE_SEP;
        }
        // /storage/emulated/0/Android/data/com.xxx/cache/TuanCanLog
        File fileDir = new File(defaultDir);
        if (!fileDir.exists()) {
            fileDir.mkdirs();
        }
        String path = fileDir.getPath();
        FilePrinter filePrinter = new FilePrinter
                .Builder(path)
                .fileNameGenerator(new DateFileNameGenerator())
                .backupStrategy(new NeverBackupStrategy())
                .flattener(new DefaultFlattener())
                .cleanStrategy(new FileLastModifiedCleanStrategy(2 * 24 * 3600 * 1000))
                .build();
        XLog.init(config, filePrinter);
        XLog.i(new SimpleDateFormat("yyyy-MM-dd").format(System.currentTimeMillis()) + "----->" + "日志初始化");
    }

    public static void d(String msg) {
        d(null, msg, null);
    }

    public static void d(String tag, String msg) {
        info(tag, msg, TYPE.debug, null);
    }

    public static void d(String tag, int msg) {
        info(tag, String.valueOf(msg), TYPE.debug, null);
    }

    public static void d(String tag, String msg, Throwable tr) {
        info(tag, msg, TYPE.debug, tr);
    }

    public static void e(String msg) {
        d(null, msg, null);
    }

    public static void e(String tag, String msg) {
        info(tag, msg, TYPE.error, null);
    }

    public static void e(String tag, int msg) {
        info(tag, String.valueOf(msg), TYPE.error, null);
    }

    public static void e(String tag, String msg, Throwable tr) {
        info(tag, msg, TYPE.error, tr);
    }

    public static void i(String msg) {
        i(null, msg, null);
    }

    public static void i(String tag, String msg) {
        info(tag, msg, TYPE.info, null);
    }

    public static void i(String tag, int msg) {
        info(tag, String.valueOf(msg), TYPE.info, null);
    }

    public static void i(String tag, Throwable tr) {
        info(tag, "", TYPE.info, tr);
    }

    public static void i(String tag, String msg, Throwable tr) {
        info(tag, msg, TYPE.info, tr);
    }

    private static void info(String tag, String msg, TYPE type, Throwable tr) {
        if (enableLog) {
            if (TextUtils.isEmpty(tag))
                tag = TAG;
            if (!tag.equals(TAG))
                tag = TAG + " " + tag;
            infoBase(tag, msg, type, tr);
        }
    }

    /**
     * 此方法谨慎修改
     * 插件配置 disableLog 会修改此方法
     *
     * @param tag String
     * @param msg String
     * @param tr  Throwable
     */
    private static void infoBase(String tag, String msg, TYPE type, Throwable tr) {
        try {
            if (msg != null) {
                byte[] bytes = msg.getBytes();
                int length = bytes.length;
                if (length <= CHUNK_SIZE) {
                    log(type, tag, msg, tr);
                } else {
                    int index = 0, lastIndexOfLF = 0;
                    //当最后一次剩余值小于 CHUNK_SIZE 时，不需要再截断
                    while (index < length - CHUNK_SIZE) {
                        lastIndexOfLF = lastIndexOfLF(bytes, index);
                        int chunkLength = lastIndexOfLF - index;
                        log(type, tag, new String(bytes, index, chunkLength), null);
                        if (chunkLength < CHUNK_SIZE) {
                            //跳过换行符
                            index = lastIndexOfLF + 1;
                        } else {
                            index = lastIndexOfLF;
                        }
                    }
                    if (length > index) {
                        log(type, tag, new String(bytes, index, length - index), tr);
                    }
                }
            } else {
                log(type, tag, null, tr);
            }
        } catch (Exception e) {
            printStackTrace(e);
        }
    }

    private static void log(TYPE type, String tag, String msg, Throwable tr) {
        if (type.equals(TYPE.info)) {
            Log.i(tag, msg, tr);
            if (useXlog)
                XLog.i(tag + " " + msg, tr);
        } else if (type.equals(TYPE.debug)) {
            Log.d(tag, msg, tr);
            if (useXlog)
                XLog.d(tag + " " + msg, tr);
        } else if (type.equals(TYPE.error)) {
            Log.e(tag, msg, tr);
            if (useXlog)
                XLog.e(tag + " " + msg, tr);
        } else {
            Log.v(tag, msg, tr);
            if (useXlog)
                XLog.v(tag + " " + msg, tr);
        }
    }

    /**
     * 获取从 fromIndex 开始，最靠近尾部的换行符
     *
     * @param bytes     日志转化的 bytes 数组
     * @param fromIndex 从 bytes 开始的下标
     * @return 换行符的下标
     */
    private static int lastIndexOfLF(byte[] bytes, int fromIndex) {
        int index = Math.min(fromIndex + CHUNK_SIZE, bytes.length - 1);
        for (int i = index; i > index - CHUNK_SIZE; i--) {
            //返回换行符的位置
            if (bytes[i] == (byte) 10) {
                return i;
            }
        }
        return index;
    }

    /**
     * 此方法谨慎修改
     * 插件配置 disableLog 会修改此方法
     *
     * @param e Exception
     */
    public static void printStackTrace(Exception e) {
        if (enableLog && e != null) {
            Log.e(TAG, "Exception", e);
            if (useXlog)
                XLog.e(TAG, "Exception", e);
        }
    }

    public static void printStackTrace(Throwable e) {
        if (enableLog && e != null) {
            Log.e(TAG, "Throwable", e);
            if (useXlog)
                XLog.e(TAG, "Throwable", e);
        }
    }

    /**
     * 设置是否打印 Log
     *
     * @param isEnableLog Log 状态
     */
    private static void setEnableLog(boolean isEnableLog) {
        enableLog = isEnableLog;
    }

    public static boolean isLogEnabled() {
        return enableLog;
    }
}
